﻿using System;
using Lypshare.Net.Client;
using Lypshare.Flow.Packets;
using Lypshare.Net;

namespace Lypshare.Flow
{
	/// <summary>
	/// Raw listener.
	/// </summary>
	/// <exception cref='ArgumentNullException'>
	/// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
	/// </exception>
	/// <exception cref='InvalidOperationException'>
	/// Is thrown when an operation cannot be performed.
	/// </exception>
    public class RawListener
    {
        // --------------------------------------------------------
        // Class Events
        // --------------------------------------------------------


        /// <summary>
        /// Occurs when data received.
        /// </summary>
        public event Action<RawListener, RawDataEventArgs> DataReceived = null;


        /// <summary>
        /// Occurs when data sent.
        /// </summary>
        public event Action<RawListener, RawDataEventArgs> DataSent = null;


        // --------------------------------------------------------
        // Class Variables
        // --------------------------------------------------------


        /// <summary>
        /// The _session.
        /// </summary>
        private INetworkSession _session = null;


        /// <summary>
        /// The _id.
        /// </summary>
        private int _id = 0;


        /// <summary>
        /// The _active.
        /// </summary>
        private bool _active = false;


        // --------------------------------------------------------
        // Class Properties
        // --------------------------------------------------------


        /// <summary>
        /// Gets the session.
        /// </summary>
        /// <value>
        /// The session.
        /// </value>
        public INetworkSession Session
        {
            get { return _session; }

        } // Property: Session


        /// <summary>
        /// Gets the identifier.
        /// </summary>
        /// <value>
        /// The identifier.
        /// </value>
        public int Id
        {
            get { return _id; }

        } // Property: Id


        /// <summary>
        /// Gets a value indicating whether this <see cref="Lypshare.Flow.RawListener"/> is active.
        /// </summary>
        /// <value>
        /// <c>true</c> if active; otherwise, <c>false</c>.
        /// </value>
        public bool Active
        {
            get { return _active; }

        } // Property: Active


        // --------------------------------------------------------
        // Class Functions
        // --------------------------------------------------------


        /// <summary>
        /// Initializes a new instance of the <see cref="Lypshare.Flow.RawListener"/> class.
        /// </summary>
        /// <param name='session'>
        /// Session.
        /// </param>
        /// <param name='id'>
        /// Identifier.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        public RawListener(INetworkSession session, int id)
        {
            if (session == null)
                throw new ArgumentNullException("session");

            _session = session;
            _id = id;

        } // Constructor


        /// <summary>
        /// Run this instance.
        /// </summary>
        /// <exception cref='InvalidOperationException'>
        /// Is thrown when an operation cannot be performed.
        /// </exception>
        public void Run()
        {
            if (Active) 
                throw new InvalidOperationException(
                    "Listener already running.");

            _active = true;

            // Link received event
            Session.DataReceived 
                += OnSessionDataReceived;

        } // Run


        /// <summary>
        /// Send the specified packet and endpoint.
        /// </summary>
        /// <param name='packet'>
        /// Packet.
        /// </param>
        /// <param name='endpoint'>
        /// Endpoint.
        /// </param>
        /// <exception cref='ArgumentNullException'>
        /// Is thrown when an argument passed to a method is invalid because it is <see langword="null" /> .
        /// </exception>
        public void Send(Packet packet, FlowEndPoint endpoint)
        {
            if (packet == null) throw new ArgumentNullException("packet");
            if (endpoint == null) throw new ArgumentNullException("endpoint");

            var rawPacket = new RawPacket(
                endpoint.Id, Id);

            rawPacket.Content = packet;
            endpoint.Connection.Send(rawPacket);

            OnDataSent(new RawDataEventArgs(
                endpoint, packet));

        } // Send


        /// <summary>
        /// Shutdown this instance.
        /// </summary>
        public void Shutdown()
        {
            // Check for state
            if (!Active) return;

            _active = false;

            // Unlink received events
            Session.DataReceived 
                -= OnSessionDataReceived;

        } // Shutdown


        // --------------------------------------------------------
        // Class Handles
        // --------------------------------------------------------


        /// <summary>
        /// Raises the session data received event.
        /// </summary>
        /// <param name='arg1'>
        /// Arg1.
        /// </param>
        /// <param name='arg2'>
        /// Arg2.
        /// </param>
        protected void OnSessionDataReceived(INetworkSession arg1, ConnectionDataEventArgs arg2)
        {
            if (arg2.Packet is RawPacket)
            {
                var rawPacket = arg2.Packet as RawPacket;

                if (rawPacket.ReceiverId == Id)
                {
                    var endpoint = new FlowEndPoint(
                        arg2.Connection, rawPacket.SenderId);

                    OnDataReceived(new RawDataEventArgs(
                        endpoint, rawPacket.Content));

                } // if (rawPacket.ReceiverId == Id)
            } // if (arg2.Packet is RawPacket)
        } // OnSessionDataReceived


        /// <summary>
        /// Raises the data sent event.
        /// </summary>
        /// <param name='e'>
        /// E.
        /// </param>
        private void OnDataSent(RawDataEventArgs e)
        {
            if (DataSent != null)
                DataSent(this, e);

        } // OnDataSent


        /// <summary>
        /// Raises the data received event.
        /// </summary>
        /// <param name='e'>
        /// E.
        /// </param>
        private void OnDataReceived(RawDataEventArgs e)
        {
            if (DataReceived != null)
                DataReceived(this, e);

        } // OnDataReceived

    } // class RawListener
} // namespace Lypshare.Flow